package com.caregiver.watch.business.data.service.impl;

import com.alibaba.fastjson2.JSON;
import com.caregiver.watch.business.data.dao.DeviceCallRecordsDao;
import com.caregiver.watch.business.data.domain.CallLogWithAlarm;
import com.caregiver.watch.business.data.domain.CallRecord;
import com.caregiver.watch.business.data.domain.DeviceCallLogs;
import com.caregiver.watch.business.data.domain.DeviceStatus;
import com.caregiver.watch.business.data.domain.ISPDeviceInfo;
import com.caregiver.watch.business.data.domain.ResponseObject;
import com.caregiver.watch.business.data.enums.CallTypeEnum;
import com.caregiver.watch.business.data.model.po.DeviceCallRecords;
import com.caregiver.watch.business.data.parser.DataParserContext;
import com.caregiver.watch.business.data.parser.IDataParser;
import com.caregiver.watch.business.data.service.DeviceDataService;
import com.caregiver.watch.business.data.utils.ByteUtils;
import com.caregiver.watch.business.data.utils.HttpHelper;
import com.caregiver.watch.business.user.enums.DeviceOnlineStatusEnum;
import com.caregiver.watch.business.user.model.po.UserDevice;
import com.caregiver.watch.business.user.service.UserDeviceService;
import com.caregiver.watch.common.utils.collection.ListUtils;
import com.caregiver.watch.common.utils.time.DateUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description: 设备服务
 * @Author 疆戟
 * @Date 2025/9/6 11:03
 * @Version 1.0
 */
@AllArgsConstructor
@Service
@Slf4j
public class DeviceDataServiceImpl implements DeviceDataService {

    private final DataParserContext dataParserContext;
    private final UserDeviceService userDeviceService;
    private final DeviceCallRecordsDao deviceCallRecordsDao;

    @Override
    public void uploadPBData(HttpServletRequest req, HttpServletResponse resp) {
        HttpHelper.printHeaders(req);
        try {
            byte[] buffer = ByteUtils.getBytes(req);
            if (Objects.isNull(buffer)) {
                return;
            }
            int contentLength = req.getContentLength();
            int offset = 0;
            // 前15个字节是设备ID/IMEI
            byte[] deviceIdBytes = new byte[15];
            System.arraycopy(buffer, offset, deviceIdBytes, 0, 15);
            String deviceId = new String(deviceIdBytes);
            log.info("deviceId: " + deviceId);
            // 获取用户ID
            Long userId = userDeviceService.queryUserIdByDeviceUuid(deviceId);
            if (Objects.isNull(userId)) {
                log.error("为获取到用户ID");
                return;
            }
            offset += 15;
            // 可能有多个数据包，需要循环解析所有数据包
            while (true) {
                if (offset > contentLength - 9) {
                    break;
                }
                // 解析包含8个字节的头部部分
                // 固定为0x4454
                byte[] prefixBytes = new byte[2];
                // 负载长度
                byte[] lengthBytes = new byte[2];
                // CRC校验
                byte[] crcBytes = new byte[2];
                /* 负载的操作码(opt)编号 opt编号: 0x0A - 设备信息/实时步数/GNSS 0x80 - 历史数据, 历史健康数据/心电图 0x00 - 设备信息 */
                byte[] optBytes = new byte[2];
                System.arraycopy(buffer, offset, prefixBytes, 0, 2);
                if (prefixBytes[0] != 0x44 || prefixBytes[1] != 0x54) {
                    break;
                }
                offset += 2;
                System.arraycopy(buffer, offset, lengthBytes, 0, 2);
                int length = ByteUtils.byteToPositiveInt(lengthBytes[1]) * 0x100 + ByteUtils.byteToPositiveInt(lengthBytes[0]);
                offset += 2;
                System.arraycopy(buffer, offset, crcBytes, 0, 2);
                offset += 2;
                System.arraycopy(buffer, offset, optBytes, 0, 2);
                int opt = (optBytes[0] | optBytes[1] << 8) & 0x00FF;
                offset += 2;
                if (length <= 0) {
                    ByteUtils.bytesToHexString(lengthBytes);
                    break;
                }
                //解析数据部分
                byte[] payload = new byte[length];
                System.arraycopy(buffer, offset, payload, 0, length);
                offset += length;
                log.info("opt:{}", opt);
                IDataParser dataParser = dataParserContext.getDataParser(opt);
                if (Objects.isNull(dataParser)) {
                    log.error("opt: " + opt + " not found");
                    continue;
                }
                dataParser.dataParse(userId, deviceId, payload);
            }
            //响应内容是文本/纯二进制
            byte[] data = new byte[]{0x00};
            HttpHelper.write(resp, data);
        } catch (Exception e) {
            log.error("解析DP数据异常:{}", e.getMessage(), e);
        }
    }

    @Override
    public void uploadDeviceInfoData(HttpServletRequest req, HttpServletResponse resp) {
        byte[] buffer = ByteUtils.getBytes(req);
        ResponseObject respObj = new ResponseObject();
        String jsonStr = new String(buffer);
        log.info("device info: " + jsonStr);
        try {
            ISPDeviceInfo deviceInfo = JSON.parseObject(jsonStr, ISPDeviceInfo.class);
            if (Objects.isNull(deviceInfo)) {
                log.error("device info is null");
                return;
            }
            Long deviceId = userDeviceService.queryDeviceIdByDeviceUuid(deviceInfo.getDeviceid());
            if (Objects.isNull(deviceId)) {
                log.error("user device is null");
                return;
            }
            UserDevice userDevice = new UserDevice();
            userDevice.setDeviceId(deviceId);
            userDevice.setDeviceUuid(deviceInfo.getDeviceid());
            userDevice.setDeviceModel(deviceInfo.getModel());
            userDevice.setSystemVersion(deviceInfo.getVersion());
            // 上报了信息设置为在线状态
            userDevice.setOnlineStatus(DeviceOnlineStatusEnum.ONLINE.getCode());
            userDeviceService.updateUserDevice(userDevice);
            respObj.setReturnCode(0);
        } catch (Exception ex) {
            log.error(ex.getMessage());
            respObj.setReturnCode(10002);
        }
        // 响应内容是文本/纯二进制
        HttpHelper.write(resp, respObj);
    }

    @Override
    public void deviceStatusChangeNotify(HttpServletRequest req, HttpServletResponse resp) {
        byte[] buffer = ByteUtils.getBytes(req);
        ResponseObject respObj = new ResponseObject();
        String jsonStr = new String(buffer);
        log.info("device status change: " + jsonStr);
        try {
            DeviceStatus deviceStatus = JSON.parseObject(jsonStr, DeviceStatus.class);
            if (Objects.isNull(deviceStatus)) {
                log.error("device status is null");
                return;
            }
            // 跟新设备状态
            Long deviceId = userDeviceService.queryDeviceIdByDeviceUuid(deviceStatus.getDeviceId());
            if (Objects.isNull(deviceId)) {
                log.error("user device is null");
                return;
            }
            UserDevice userDevice = new UserDevice();
            userDevice.setDeviceId(deviceId);
            DeviceOnlineStatusEnum status = DeviceOnlineStatusEnum.parse(deviceStatus.getStatus());
            // 如果是离线需要跟新最后在线时间
            if (status == DeviceOnlineStatusEnum.OFFLINE) {
                userDevice.setLastActiveTime(deviceStatus.getEventTime());
            }
            // 更新设备在线状态
            userDevice.setOnlineStatus(deviceStatus.getStatus());
            userDeviceService.updateUserDevice(userDevice);
            respObj.setReturnCode(0);
        } catch (Exception ex) {
            log.error(ex.getMessage());
            respObj.setReturnCode(10002);
        }
        // 响应内容是文本/纯二进制
        HttpHelper.write(resp, respObj);
    }

    @Override
    public void uploadAlarmData(HttpServletRequest req, HttpServletResponse resp) {
        if (log.isDebugEnabled()) {
            log.debug("uploadAlarmData");
        }
        HttpHelper.printHeaders(req);
        int contentLength = req.getContentLength();
        byte[] buffer = ByteUtils.getBytes(req);
        ByteUtils.bytesToHexString(buffer);

        int offset = 0;
        //the first 15 bytes is deviceid/imei
        byte[] deviceId_bytes = new byte[15];
        System.arraycopy(buffer, offset, deviceId_bytes, 0, 15);
        String deviceId = new String(deviceId_bytes);
        log.info("deviceid: " + deviceId);
        // 获取用户ID
        Long userId = userDeviceService.queryUserIdByDeviceUuid(deviceId);
        offset += 15;
        //there maybe multiple data packages possible, need loop to parse all
        while (true) {
            if (offset > contentLength - 9) {
                break;
            }
            byte[] prefixBytes = new byte[2];//fixed to 0x4454
            byte[] length_bytes = new byte[2];//length of the payload
            byte[] crc_bytes = new byte[2];//crc check
            /*
            the opt number of the payload
            opt number:
            0x12 - device alarm
             */
            byte[] optBytes = new byte[2];
            System.arraycopy(buffer, offset, prefixBytes, 0, 2);
            if (prefixBytes[0] != 0x44 || prefixBytes[1] != 0x54) {
                break;
            }
            offset += 2;
            System.arraycopy(buffer, offset, length_bytes, 0, 2);
            int length = length_bytes[1] * 0x100 + length_bytes[0];
            offset += 2;
            System.arraycopy(buffer, offset, crc_bytes, 0, 2);
            offset += 2;
            System.arraycopy(buffer, offset, optBytes, 0, 2);
            int opt = (optBytes[0] | optBytes[1] << 8) & 0x00FF;
            offset += 2;
            byte[] payload = new byte[length];
            System.arraycopy(buffer, offset, payload, 0, length);
            offset += length;
            log.info("opt:{}", opt);
            IDataParser dataParser = dataParserContext.getDataParser(opt);
            if (Objects.isNull(dataParser)) {
                log.error("opt: " + opt + " not found");
                continue;
            }
            dataParser.dataParse(userId, deviceId, payload);
        }
        //reponse content is text/plain, binary
        byte[] data = new byte[]{0x00};
        HttpHelper.write(resp, data);
    }

    @Override
    public void uploadSosCallLogData(HttpServletRequest req, HttpServletResponse resp) {
        if (log.isDebugEnabled()) {
            log.debug("uploadSosCallLogData");
        }
        byte[] buffer = ByteUtils.getBytes(req);;
        ResponseObject respObj = new ResponseObject();
        String jsonStr = new String(buffer);
        log.info("call log: " + jsonStr);
        try {
            DeviceCallLogs callLog = JSON.parseObject(jsonStr, DeviceCallLogs.class);
            handleCallLog(callLog);
            respObj.setReturnCode(0);
        } catch (Exception ex) {
            log.error(ex.getMessage());
            respObj.setReturnCode(10002);
        }
        HttpHelper.write(resp, JSON.toJSONBytes(respObj));
    }


    /**
     * 处理通话数据
     * @param callLog
     * @return
     */
    private void handleCallLog(DeviceCallLogs callLog) {
        String deviceId = callLog.getDeviceId();
        Long userId = userDeviceService.queryUserIdByDeviceUuid(deviceId);
        List<DeviceCallRecords> insertList = new ArrayList<>();
        // 处理正常通话记录
        for (CallRecord callRecord : ListUtils.ofNullable(callLog.getNormalCallLogs(), new ArrayList<>())) {
            DeviceCallRecords records =  buildDeviceCallRecords(callRecord, userId, deviceId);
            records.setCallType(CallTypeEnum.NORMAL_CALL);
            insertList.add(records);
        }
        // 处理sos通话记录
        for (CallLogWithAlarm callLogWithAlarm : ListUtils.ofNullable(callLog.getSos(), new ArrayList<>())) {
            LocalDateTime alarmTime = DateUtils.parseLocalDateTime(callLogWithAlarm.getAlarmTime());
            if (CollectionUtils.isEmpty(callLogWithAlarm.getCallLogs())) {
                continue;
            }
            for (CallRecord callRecord : callLogWithAlarm.getCallLogs()) {
                DeviceCallRecords records =  buildDeviceCallRecords(callRecord, userId, deviceId);
                records.setCallType(CallTypeEnum.SOS_CALL);
                records.setAlarmTime(DateUtils.parseLocalDateTime(callLogWithAlarm.getAlarmTime()));
                records.setLat(callLogWithAlarm.getLat());
                records.setLon(callLogWithAlarm.getLon());
                records.setAlarmTime(alarmTime);
                insertList.add(records);
            }
        }
        ListUtils.partition(insertList, 1000).forEach(deviceCallRecordsDao::batchInsert);
    }

    /**
     * 构建设备通话记录
     * @param callRecord
     * @param userId
     * @param deviceId
     * @return
     */
    private DeviceCallRecords buildDeviceCallRecords(CallRecord callRecord, Long userId, String deviceId) {
        DeviceCallRecords records = new DeviceCallRecords();
        records.setUserId(userId);
        records.setDeviceUuid(deviceId);
        records.setCallStatus(callRecord.getStatus());
        records.setPhoneNumber(callRecord.getCallNumber());
        records.setStartTime(DateUtils.parseLocalDateTime(callRecord.getStartTime()));
        records.setEndTime(DateUtils.parseLocalDateTime(callRecord.getEndTime()));
        return records;
    }
}
